/**
 * @file timer_clock.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include <stdio.h>
#include "lv_watch.h"

#if USE_LV_WATCH_TIMER

/*********************
 *      DEFINES
 *********************/
#define TIMER_TIME_STR_LENGTH       12
#define TIMER_PERIODIC_TIME_LENGTH  100  //ms

/**********************
 *  STATIC VARIABLES
**********************/
static const timer_item_t timer_item[] = {
    {TIMER_1_MIN, WATCH_TEXT_ID_TIMER_1_MIN, 60*1000},
    {TIMER_3_MIN, WATCH_TEXT_ID_TIMER_3_MIN, 60*3*1000},
    {TIMER_5_MIN, WATCH_TEXT_ID_TIMER_5_MIN, 60*5*1000},
    {TIMER_10_MIN, WATCH_TEXT_ID_TIMER_10_MIN, 60*10*1000},
    {TIMER_15_MIN, WATCH_TEXT_ID_TIMER_15_MIN, 60*15*1000},
    {TIMER_30_MIN, WATCH_TEXT_ID_TIMER_30_MIN, 60*30*1000},
    {TIMER_1_HOUR, WATCH_TEXT_ID_TIMER_1_HOUR, 60*60*1000},
    {TIMER_2_HOUR, WATCH_TEXT_ID_TIMER_2_HOUR, 60*120*1000},
    {TIMER_CUSTOM, WATCH_TEXT_ID_TIMER_CUSTOM, 0},
};

static uint32_t tick_start = 0;
static uint32_t tick_end = 0;
static uint32_t tick_elapsed = 0;

static timer_state_t timer_state = TIMER_STATE_IDLE;
static uint32_t timer_total = 0; //ms
static int32_t timer_remaining = 0; //ms

void * timer_up_id = NULL;
void * timer_period_id = NULL;
#ifndef BUILD_IN_PC_SIMULATOR
TASK_HANDLE * timer_task_handle = NULL;
#endif

/**********************
 *  STATIC PROTOTYPES
 **********************/
//timer list
static lv_timer_list_obj_ext_t * timer_list_get_ext(void);
static void timer_list_prepare_destory(lv_obj_t * activity_obj);
static lv_obj_t * timer_list_create(lv_obj_t * activity_obj);
static void timer_list_btn_action(lv_obj_t * obj, lv_event_t e);

//timer
static lv_timer_obj_ext_t * timer_get_ext(void);
static void timer_prepare_destory(lv_obj_t * activity_obj);
static lv_obj_t * timer_create(lv_obj_t * activity_obj);
static void timer_label_anim(lv_obj_t * obj);
static void timer_label_anim_cb(lv_anim_t * anim);
static void timer_arc_anim(lv_obj_t * obj);
static void timer_arc_anim_cb(lv_anim_t * anim);
static void timer_get_time_remaining_str(int32_t time, char *str, uint8_t size);
static int32_t timer_get_time_remaining(void);
static uint32_t timer_tick_to_mseconds(uint32_t tick);
static void timer_stop_action(lv_obj_t * btn, lv_event_t e);
static void timer_start_action(lv_obj_t * btn, lv_event_t e);
static void timer_pause_action(lv_obj_t * btn, lv_event_t e);
#ifdef BUILD_IN_PC_SIMULATOR
static void timer_peroid_cb(void * param);
static void timer_timeup_cb(void * param);
#else
static void timer_peroid_cb(uint32_t param);
static void timer_timeup_cb(uint32_t param);
static void timer_task(void * para);
static void timer_task_async_cb(void *para);
#endif
static void timer_audio_ctrl_callback(AUDIO_CTRL_PRIORITY priority);

//set custom time
static void timer_set_time_create(void);
static lv_obj_t * timer_set_time_roller_create(lv_obj_t * par, hal_rtc_t * date_time);
static void timer_set_time_roller_action(lv_obj_t * roller, lv_event_t e);
static lv_obj_t * timer_set_time_opt_create(lv_obj_t * par);
static void timer_set_time_cancel_btn_action(lv_obj_t * obj, lv_event_t e);
static void timer_set_time_ok_btn_action(lv_obj_t * obj, lv_event_t e);
static void timer_get_optstr_from_num(uint8_t num, char * stropt, uint8_t type);

/**********************
 *   GLOBAL FUNCTIONS
***********************/
void timer_create_event_cb(lv_obj_t * btn, lv_event_t e)
{
    (void)btn;

    if(LV_EVENT_CLICKED == e) {
        if(TIMER_STATE_IDLE == timer_state) {
            timer_list_create(NULL);
        } else {
            timer_create(NULL);
        }
    }
}

/**********************
*   STATIC FUNCTIONS
**********************/
static lv_timer_list_obj_ext_t * timer_list_get_ext(void)
{
    lv_timer_list_obj_ext_t  * ext = NULL;

    lv_obj_t * watch_obj = NULL;
    lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_TIMER_LIST);
    if(activity_obj) {
        lv_watch_get_child_obj(activity_obj, lv_watch_obj_signal, &watch_obj);

        if(watch_obj) {
            ext = lv_obj_get_ext_attr(watch_obj);
        }
    }

    return ext;
}

static void timer_list_prepare_destory(lv_obj_t * activity_obj)
{
    lv_watch_png_cache_all_free();
}

lv_obj_t * timer_list_create(lv_obj_t * activity_obj)
{
    /* activity obj */
    if(NULL == activity_obj) {
        lv_watch_activity_ext_t activity_ext;
        memset(&activity_ext, 0, sizeof(lv_watch_activity_ext_t));
        activity_ext.actId = ACT_ID_TIMER_LIST;
        activity_ext.create = timer_list_create;
        activity_ext.prepare_destory = timer_list_prepare_destory;
        activity_obj = lv_watch_creat_activity_obj(&activity_ext);
    }

    /* watch obj */
    lv_obj_t * obj = lv_watch_obj_create(activity_obj);
    lv_timer_list_obj_ext_t * ext = lv_obj_allocate_ext_attr(obj, sizeof(lv_timer_list_obj_ext_t));
    LV_ASSERT_MEM(ext);

    lv_obj_t * title = lv_label_create(obj, NULL);
    lv_label_set_text_id(title, WATCH_TEXT_ID_TIMER);
    lv_obj_set_size(title, LV_HOR_RES, LV_VER_RES/5);
    lv_obj_add_style(title, LV_LABEL_PART_MAIN, &lv_watch_font20_gray);
    lv_obj_align(title, NULL, LV_ALIGN_IN_TOP_MID, 0, 10);

    lv_obj_t * page  = lv_page_create(obj, NULL);
    lv_obj_set_size(page, LV_HOR_RES, LV_VER_RES*4/5);
    lv_obj_align(page, title, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
    lv_obj_add_style(page, LV_PAGE_PART_BG, &lv_style_transp);
    lv_obj_add_style(page, LV_PAGE_PART_SCROLLABLE, &lv_style_transp);
    lv_obj_add_style(page, LV_PAGE_PART_EDGE_FLASH, &lv_watch_cont_opa1);
    lv_page_set_scrollbar_mode(page, LV_SCROLLBAR_MODE_OFF);

    lv_obj_t * cont = lv_cont_create(page, NULL);
    uint8_t item_col = 2;
    uint8_t item_row = (TIMER_ITEM_NUM % item_col == 0) ? (TIMER_ITEM_NUM / item_col) : (TIMER_ITEM_NUM / item_col + 1);
    uint8_t item_h = LV_VER_RES*130/360;
    uint8_t item_w = item_h;
    uint8_t item_pad_inner = LV_HOR_RES/24;
    lv_obj_set_size(cont, LV_HOR_RES, item_row * (item_h + item_pad_inner) - item_pad_inner);
    lv_cont_set_layout(cont, LV_LAYOUT_PRETTY_TOP);
    lv_obj_set_click(cont, false);
    lv_obj_set_style_local_bg_opa(cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
    lv_obj_set_style_local_pad_left(cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_HOR_RES/10);
    lv_obj_set_style_local_pad_right(cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_HOR_RES/10);
    lv_obj_set_style_local_pad_top(cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_pad_bottom(cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_pad_inner(cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_HOR_RES/24);
    lv_watch_obj_add_element(cont);
    lv_page_glue_obj(cont, true);

    lv_obj_t * btn;
    lv_obj_t * label;
    for(uint8_t i = 0; i < TIMER_ITEM_NUM; i++) {
        btn = lv_btn_create(cont, NULL);
        lv_obj_add_protect(btn, LV_PROTECT_PRESS_LOST);
        lv_obj_set_size(btn, item_w, item_h);
        lv_obj_set_event_cb(btn, timer_list_btn_action);
        lv_watch_set_free_num(btn, i);

        label = lv_label_create(btn, NULL);
        lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
        lv_label_set_long_mode(label, LV_LABEL_LONG_BREAK);
        lv_obj_set_width(label, lv_obj_get_width(btn));
        lv_label_set_text_id(label, timer_item[i].txt_id);
        lv_obj_set_click(label, false);

        lv_watch_obj_add_element(btn);
        lv_page_glue_obj(btn, true);
    }

    return obj;
}

static void timer_list_btn_action(lv_obj_t * obj, lv_event_t e)
{
    if(LV_EVENT_CLICKED != e) {
        return;
    }

    timer_num_t timer_num = (timer_num_t)lv_watch_get_free_num(obj);
    printf("%s: num = %d\n", __FUNCTION__, timer_num);

    if(TIMER_CUSTOM == timer_num) {
        timer_set_time_create();
    } else {
        //del timer list UI
        lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_TIMER_LIST);
        if(activity_obj) {
            timer_list_prepare_destory(activity_obj);
            lv_obj_del(activity_obj);
        }

        //create timer UI
        timer_total = timer_item[timer_num].duration;
        timer_remaining = timer_total;
        timer_create(NULL);
    }
}

static lv_timer_obj_ext_t * timer_get_ext(void)
{
    lv_timer_obj_ext_t  * ext = NULL;

    lv_obj_t * watch_obj = NULL;
    lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_TIMER);
    if(activity_obj) {
        lv_watch_get_child_obj(activity_obj, lv_watch_obj_signal, &watch_obj);

        if(watch_obj) {
            ext = lv_obj_get_ext_attr(watch_obj);
        }
    }

    return ext;
}

static void timer_prepare_destory(lv_obj_t * activity_obj)
{
    lv_watch_png_cache_all_free();

    if(TIMER_STATE_TIMEOUT == timer_state) {
        watch_set_suspend_enable(true, ACT_ID_TIMER, 0);

        Hal_Vibrator_Play_End();
        Hal_Tone_Play_End();
        Hal_Audio_Manage_End_Ind(AUDIO_CTRL_PRIORITY_TIMER);

        timer_state = TIMER_STATE_IDLE;
    }

    if(TIMER_STATE_IDLE == timer_state) {
        tick_start = 0;
        tick_end = 0;
        tick_elapsed = 0;
        timer_total = 0;
        timer_remaining = 0;
        timer_up_id = NULL;
    }
}

static lv_obj_t * timer_create(lv_obj_t * activity_obj)
{
    /* activity obj */
    if(NULL == activity_obj) {
        lv_watch_activity_ext_t activity_ext;
        memset(&activity_ext, 0, sizeof(lv_watch_activity_ext_t));
        activity_ext.actId = ACT_ID_TIMER;
        activity_ext.create = timer_create;
        activity_ext.prepare_destory = timer_prepare_destory;
        activity_obj = lv_watch_creat_activity_obj(&activity_ext);
    } else {
        lv_obj_clean(activity_obj);
    }

    /* watch obj */
    lv_obj_t * obj = lv_watch_obj_create(activity_obj);
    lv_timer_obj_ext_t * ext = lv_obj_allocate_ext_attr(obj, sizeof(lv_timer_obj_ext_t));
    LV_ASSERT_MEM(ext);

    //Create an Arc
    lv_obj_t * arc = lv_arc_create(obj, NULL);
    lv_arc_set_bg_angles(arc, 0, 360);
    uint16_t angle_end = 270 + 360 - 360 * (timer_total - timer_remaining) / timer_total;
    lv_arc_set_angles(arc, 270, angle_end);
    lv_obj_set_size(arc, LV_VER_RES-10, LV_VER_RES-10);
    lv_obj_align(arc, NULL, LV_ALIGN_CENTER, 0, 0);
    lv_obj_set_style_local_line_color(arc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_CYAN);
    lv_obj_set_style_local_line_width(arc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_DPX(10));
    lv_obj_set_style_local_line_color(arc, LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_obj_set_style_local_line_width(arc, LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_DPX(10));
    lv_obj_set_style_local_line_opa(arc, LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_OPA_50);
    lv_obj_set_style_local_bg_opa(arc, LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_OPA_0);
    lv_watch_obj_add_element(arc);
    ext->arc_time_remaining = arc;

    //time remaining
    char str[TIMER_TIME_STR_LENGTH];
    if(TIMER_STATE_RUN == timer_state) {
        tick_end = Hal_Timer_Get_Counter();
        timer_remaining = timer_get_time_remaining();
    }
    timer_get_time_remaining_str(timer_remaining, str, TIMER_TIME_STR_LENGTH);

    lv_obj_t * label_time_remaining = lv_label_create(obj, NULL);
    lv_label_set_text(label_time_remaining, str);
    lv_obj_add_style(label_time_remaining, LV_LABEL_PART_MAIN, &lv_watch_font30);
    lv_obj_align(label_time_remaining, NULL, LV_ALIGN_CENTER, 0, 0);
    lv_obj_set_base_dir(label_time_remaining, LV_BIDI_DIR_LTR);
    ext->label_time_remaining = label_time_remaining;

    lv_obj_t * label_info = lv_label_create(obj, NULL);
    if(TIMER_STATE_TIMEOUT == timer_state) {
       lv_label_set_text_id(label_info, WATCH_TEXT_ID_TIMER_IS_UP);
    } else {
        lv_label_set_text_id(label_info, WATCH_TEXT_ID_TIMER);
    }
    lv_obj_add_style(label_info, LV_LABEL_PART_MAIN, &lv_watch_font20);
    lv_obj_align(label_info, label_time_remaining, LV_ALIGN_OUT_TOP_MID, 0, -10);
    ext->label_info = label_info;

    //stop btn
    lv_obj_t * btn_stop = lv_imgbtn_create(obj, NULL);
    lv_imgbtn_set_src(btn_stop, LV_BTN_STATE_RELEASED, ICON_TIMER_STOP);
    lv_imgbtn_set_src(btn_stop, LV_BTN_STATE_PRESSED, ICON_TIMER_STOP);
    lv_obj_align(btn_stop, NULL, LV_ALIGN_IN_BOTTOM_LEFT, LV_HOR_RES*90/360, -LV_VER_RES*40/360);
    lv_obj_set_event_cb(btn_stop, timer_stop_action);
    lv_watch_obj_add_element(btn_stop);

    //start btn
    lv_obj_t * btn_start = lv_imgbtn_create(obj, NULL);
    if(TIMER_STATE_TIMEOUT == timer_state) {
        lv_imgbtn_set_src(btn_start, LV_BTN_STATE_RELEASED, ICON_TIMER_RESTART);
        lv_imgbtn_set_src(btn_start, LV_BTN_STATE_PRESSED, ICON_TIMER_RESTART);
    } else {
        lv_imgbtn_set_src(btn_start, LV_BTN_STATE_RELEASED, ICON_TIMER_START);
        lv_imgbtn_set_src(btn_start, LV_BTN_STATE_PRESSED, ICON_TIMER_START);
    }
    lv_obj_align(btn_start, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, -LV_HOR_RES*90/360, -LV_VER_RES*40/360);
    lv_obj_set_event_cb(btn_start, timer_start_action);
    lv_watch_obj_add_element(btn_start);
    ext->btn_start = btn_start;

    //pause btn
    lv_obj_t * btn_pause = lv_imgbtn_create(obj, NULL);
    lv_imgbtn_set_src(btn_pause, LV_BTN_STATE_RELEASED, ICON_TIMER_PAUSE);
    lv_imgbtn_set_src(btn_pause, LV_BTN_STATE_PRESSED, ICON_TIMER_PAUSE);
    lv_obj_align(btn_pause, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, -LV_HOR_RES*90/360, -LV_VER_RES*40/360);
    lv_obj_set_event_cb(btn_pause, timer_pause_action);
    lv_watch_obj_add_element(btn_pause);
    ext->btn_pause = btn_pause;

    if(TIMER_STATE_RUN == timer_state) {
        timer_arc_anim(ext->arc_time_remaining);
        timer_label_anim(ext->label_time_remaining);
        lv_obj_set_hidden(btn_start, true);
    } else {
        lv_obj_set_hidden(btn_pause, true);
    }

    if(TIMER_STATE_TIMEOUT == timer_state) {
        watch_set_suspend_enable(false, ACT_ID_TIMER, 0);
    }

    return obj;
}

static void timer_label_anim(lv_obj_t * obj)
{
    lv_anim_t a;
    lv_anim_path_t path;

    lv_anim_path_init(&path);
    lv_anim_path_set_cb(&path, lv_anim_path_step);

    lv_anim_init(&a);
    lv_anim_set_var(&a, obj);
    lv_anim_set_start_cb(&a, (lv_anim_ready_cb_t)timer_label_anim_cb);
    lv_anim_set_path(&a, &path);
    lv_anim_set_time(&a, 1);
    lv_anim_set_playback_time(&a, 0);
    lv_anim_set_playback_delay(&a, 0);
    lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
    lv_anim_set_repeat_delay(&a, TIMER_PERIODIC_TIME_LENGTH);

    lv_anim_start(&a);
}

static void timer_label_anim_cb(lv_anim_t * anim)
{
    char str[TIMER_TIME_STR_LENGTH];

    if(timer_remaining <= 0) {
        lv_label_set_text(anim->var, "00:00");
        return;
    }

    timer_get_time_remaining_str(timer_remaining, str, TIMER_TIME_STR_LENGTH);

    if(anim->var) {
        lv_label_set_text(anim->var, str);
        lv_obj_align(anim->var, NULL, LV_ALIGN_CENTER, 0, 0);
    }
}

static void timer_arc_anim(lv_obj_t * obj)
{
    lv_anim_t a;
    lv_anim_path_t path;

    lv_anim_path_init(&path);
    lv_anim_path_set_cb(&path, lv_anim_path_step);

    lv_anim_init(&a);
    lv_anim_set_var(&a, obj);
    lv_anim_set_start_cb(&a, (lv_anim_ready_cb_t)timer_arc_anim_cb);
    lv_anim_set_path(&a, &path);
    lv_anim_set_time(&a, 1);
    lv_anim_set_playback_time(&a, 0);
    lv_anim_set_playback_delay(&a, 0);
    lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
    lv_anim_set_repeat_delay(&a, TIMER_PERIODIC_TIME_LENGTH);

    lv_anim_start(&a);
}

static void timer_arc_anim_cb(lv_anim_t * anim)
{
    if(timer_remaining <= 0) {
        lv_arc_set_end_angle(anim->var, 270);
        return;
    }

    int16_t a = 270 + 360;
    uint32_t angel_reduce = 360 * (timer_total - timer_remaining) / timer_total;
    a -= angel_reduce;

    if(anim->var) {
        lv_arc_set_end_angle(anim->var, a);
    }
}

static void timer_get_time_remaining_str(int32_t time, char *str, uint8_t size)
{
    if(!str) return;
    Hal_Mem_Set(str, 0, size);

    if(0 != time % 1000) {
        time += 1000;
    }
    uint8_t sec  = (time / 1000) % 60;         /*[0,59]*/
    uint8_t min  = (time / 1000 / 60) % 60;    /*[0,59]*/
    uint8_t hour = (time / 1000 / 3600) % 100; /*[0,99]*/

    if(hour == 0) {
        snprintf(str, size, "%.2d:%.2d", min, sec);
    } else {
        snprintf(str, size, "%.2d:%.2d:%.2d", hour, min, sec);
    }
}

static int32_t timer_get_time_remaining(void)
{
    uint32_t tick = 0;
    uint32_t mseconds = 0;

    if(tick_start > tick_end) {
        tick = 0xFFFFFFFF - tick_start + tick_end + tick_elapsed;
    } else {
        tick = tick_end - tick_start + tick_elapsed;
    }

    if (0 != tick) {
        mseconds = timer_tick_to_mseconds(tick);
    }
    //printf("%s: start=%d, end=%d, saved=%d, tick=%d, mseconds=%d\n", __FUNCTION__, tick_start, tick_end, tick_elapsed, tick, mseconds);
    mseconds = timer_total - mseconds;

    return mseconds;
}

static uint32_t timer_tick_to_mseconds(uint32_t tick)
{
    return((tick / MICRO_SEC_TO_CLOCK_TICKS_CLK_32KHZ(0xFFFFFFFF)) * (0xFFFFFFFF / 1000)
           + CLOCK_TICKS_TO_MICRO_SEC_CLK_32KHZ(tick) / 1000);
}

static void timer_stop_action(lv_obj_t * btn, lv_event_t e)
{
    if(LV_EVENT_CLICKED != e) {
        return;
    }
    printf("%s\n", __FUNCTION__);

    //stop timer
#ifdef BUILD_IN_PC_SIMULATOR
    Hal_Timer_Stop(timer_period_id);
    timer_period_id = NULL;

    Hal_Timer_Stop(timer_up_id);
    timer_up_id = NULL;
#else
    uos_timer_stop(timer_period_id);
    uos_timer_delete(timer_period_id);
    timer_period_id = NULL;

    uos_timer_stop(timer_up_id);
    uos_timer_delete(timer_up_id);
    timer_up_id = NULL;

    //del timer task
    uos_delete_task(timer_task_handle);
    timer_task_handle = NULL;
#endif

    //deal with TIMEOUT state
    if(TIMER_STATE_TIMEOUT == timer_state) {
        watch_set_suspend_enable(true, ACT_ID_TIMER, 0);

        Hal_Vibrator_Play_End();
        Hal_Tone_Play_End();
        Hal_Audio_Manage_End_Ind(AUDIO_CTRL_PRIORITY_TIMER);
    }

    //set state
    timer_state = TIMER_STATE_IDLE;

    //del timer UI
    lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_TIMER);
    if(activity_obj) {
        timer_prepare_destory(activity_obj);
        lv_obj_del(activity_obj);
    }

    //create timer list UI
    timer_list_create(NULL);
}

static void timer_start_action(lv_obj_t * btn, lv_event_t e)
{
    if(LV_EVENT_CLICKED != e) {
        return;
    }

    //set tick_start
    tick_start = Hal_Timer_Get_Counter();

    //start timer
#ifdef BUILD_IN_PC_SIMULATOR
    if(NULL == timer_period_id) {
        timer_period_id = Hal_Timer_Start(TIMER_PERIODIC_TIME_LENGTH, timer_peroid_cb, NULL, 1);
    }
    if(NULL == timer_up_id) {
        timer_up_id = Hal_Timer_Start(timer_remaining, timer_timeup_cb, NULL, 0);
    }
#else
    if(NULL == timer_period_id) {
        uos_timer_create(&timer_period_id);
        uos_timer_start(timer_period_id, MS_TO_TICKS(TIMER_PERIODIC_TIME_LENGTH),  MS_TO_TICKS(TIMER_PERIODIC_TIME_LENGTH), timer_peroid_cb, 0);
    }
    if(NULL == timer_up_id) {
        uos_timer_create(&timer_up_id);
        uos_timer_start(timer_up_id, MS_TO_TICKS(timer_remaining),  0, timer_timeup_cb, 0);
    }

    //create timer task
    if(NULL == timer_task_handle) {
        timer_task_handle = uos_create_task(timer_task, NULL, TASK_MSGQ_SIZE_16, 0x2000, 240, "timer_task");
    }
#endif

    printf("%s: timer_remaining = %d ms\n", __FUNCTION__, timer_remaining);

    lv_timer_obj_ext_t *ext = timer_get_ext();

    //deal with TIMEOUT state
    if(TIMER_STATE_TIMEOUT == timer_state) {
        watch_set_suspend_enable(true, ACT_ID_TIMER, 0);

        Hal_Vibrator_Play_End();
        Hal_Tone_Play_End();
        Hal_Audio_Manage_End_Ind(AUDIO_CTRL_PRIORITY_TIMER);

        lv_imgbtn_set_src(ext->btn_start, LV_BTN_STATE_RELEASED, ICON_TIMER_START);
        lv_imgbtn_set_src(ext->btn_start, LV_BTN_STATE_PRESSED, ICON_TIMER_START);
    }

    //set state
    timer_state = TIMER_STATE_RUN;

    //show pause btn
    lv_label_set_text_id(ext->label_info, WATCH_TEXT_ID_TIMER);
    lv_obj_align(ext->label_info, ext->label_time_remaining, LV_ALIGN_OUT_TOP_MID, 0, -10);
    lv_obj_set_hidden(ext->btn_start, true);
    lv_obj_set_hidden(ext->btn_pause, false);

    //create arc anim to update the angles of the arc.
    timer_arc_anim(ext->arc_time_remaining);

    //create label anim to update time remaining.
    timer_label_anim(ext->label_time_remaining);
}

static void timer_pause_action(lv_obj_t * btn, lv_event_t e)
{
    if(LV_EVENT_CLICKED != e) {
        return;
    }

    //set tick_end
    tick_end = Hal_Timer_Get_Counter();

    //stop timer
#ifdef BUILD_IN_PC_SIMULATOR
    Hal_Timer_Stop(timer_period_id);
    timer_period_id = NULL;

    Hal_Timer_Stop(timer_up_id);
    timer_up_id = NULL;
#else
    uos_timer_stop(timer_period_id);
    uos_timer_delete(timer_period_id);
    timer_period_id = NULL;

    uos_timer_stop(timer_up_id);
    uos_timer_delete(timer_up_id);
    timer_up_id = NULL;

    //del timer task
    uos_delete_task(timer_task_handle);
    timer_task_handle = NULL;
#endif

    //set state
    timer_state = TIMER_STATE_PAUSE;

    //set remaining time
    if(tick_start > tick_end) {
        tick_elapsed += 0xFFFFFFFF - tick_start + tick_end;
    } else {
        tick_elapsed += tick_end - tick_start;
    }
    timer_remaining = timer_total - timer_tick_to_mseconds(tick_elapsed);
    printf("%s: timer_remaining = %d ms\n", __FUNCTION__, timer_remaining);

    //show start btn
    lv_timer_obj_ext_t *ext = timer_get_ext();
    lv_obj_set_hidden(ext->btn_pause, true);
    lv_obj_set_hidden(ext->btn_start, false);

    //del anim
    lv_anim_del(ext->label_time_remaining, NULL);
    lv_anim_del(ext->arc_time_remaining, NULL);

    char str[TIMER_TIME_STR_LENGTH];
    timer_get_time_remaining_str(timer_remaining, str, TIMER_TIME_STR_LENGTH);
    lv_label_set_text(ext->label_time_remaining, str);
    lv_obj_align(ext->label_time_remaining, NULL, LV_ALIGN_CENTER, 0, 0);
}

#ifdef BUILD_IN_PC_SIMULATOR
static void timer_peroid_cb(void * param)
#else
static void timer_peroid_cb(uint32_t param)
#endif
{
    if(TIMER_STATE_RUN != timer_state) {
        return;
    }

    tick_end = Hal_Timer_Get_Counter();
    timer_remaining = timer_get_time_remaining(); //ms
}

#ifdef BUILD_IN_PC_SIMULATOR
static void timer_timeup_cb(void * param)
#else
static void timer_timeup_cb(uint32_t param)
#endif
{
    printf("%s\n", __FUNCTION__);

    tick_start = 0;
    tick_end = 0;
    tick_elapsed = 0;
    timer_remaining = timer_total;
    timer_state = TIMER_STATE_TIMEOUT;
    timer_up_id = NULL;

#ifdef BUILD_IN_PC_SIMULATOR
    //stop periodic timer
    Hal_Timer_Stop(timer_period_id);
    timer_period_id = NULL;

    if(AUDIO_CTRL_PRIORITY_TIMER == Hal_Audio_Manage_Start_Req(AUDIO_CTRL_PRIORITY_TIMER, timer_audio_ctrl_callback)) {
        //play tone
        Hal_NFFS_File_Play_Repeat(AUDIO_TIMER, query_current_volume());

        //keep vibrating
        Hal_Vibrator_Play_Repeat();

        //wakeup lcd
        watch_wakeup_lcd(true);

        //recreate timer UI
        timer_create(lv_watch_get_activity_obj(ACT_ID_TIMER));
    }
#else
    //send event to timer task
    UI_EVENT dm_evt = {0, 0, 0, 0};
    dm_evt.nEventId = 1;
    uos_send_event(timer_task_handle, &dm_evt, UOS_WAIT_FOREVER, UOS_EVENT_PRI_NORMAL);
#endif
}

#ifndef BUILD_IN_PC_SIMULATOR
static void timer_task(void * para)
{
    UI_EVENT dm_evt;
    uint8_t timeout = 0;

    while (1) {
	uos_wait_event(timer_task_handle, &dm_evt, UOS_WAIT_FOREVER);

	switch (dm_evt.nEventId) {
	case 1:
            //stop periodic timer
            uos_timer_stop(timer_period_id);
            uos_timer_delete(timer_period_id);
            timer_period_id = NULL;

            //wakup
            if(AUDIO_CTRL_PRIORITY_TIMER == Hal_Audio_Manage_Start_Req(AUDIO_CTRL_PRIORITY_TIMER, timer_audio_ctrl_callback)) {
                if(0 == Hal_Pm_Get_State()) {
                    Hal_Pm_WakeUp();
                }
            }

            timeout = 1;
            break;

        default:
            break;
        }

        if(1 == timeout) {
            break;
        }
    }

    lv_async_call(timer_task_async_cb, NULL);
}

static void timer_task_async_cb(void *para)
{
    printf("%s\n", __FUNCTION__);

    //delete timer task
    uos_delete_task(timer_task_handle);
    timer_task_handle = NULL;

    if(AUDIO_CTRL_PRIORITY_TIMER == Hal_Audio_Manage_Start_Req(AUDIO_CTRL_PRIORITY_TIMER, timer_audio_ctrl_callback)) {
        //play tone
        Hal_NFFS_File_Play_Repeat(AUDIO_TIMER, query_current_volume());

        //keep vibrating
        Hal_Vibrator_Play_Repeat();

        //wakeup lcd
        watch_wakeup_lcd(true);

        //recreate timer UI
        timer_create(lv_watch_get_activity_obj(ACT_ID_TIMER));
    }
}
#endif

static void timer_audio_ctrl_callback(AUDIO_CTRL_PRIORITY priority)
{
    printf("%s, priority is %d\n",  __FUNCTION__, priority);

    if(AUDIO_CTRL_PRIORITY_TIMER == priority) {
        //play tone
        Hal_NFFS_File_Play_Repeat(AUDIO_TIMER, query_current_volume());

        //keep vibrating
        Hal_Vibrator_Play_Repeat();

        //wakeup lcd
        watch_wakeup_lcd(true);

        //recreate timer UI
        timer_create(lv_watch_get_activity_obj(ACT_ID_TIMER));
    }
    else if(AUDIO_CTRL_PRIORITY_TIMER > priority) {
        lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_TIMER);

        if(activity_obj) {
            timer_prepare_destory(activity_obj);
            lv_obj_del(activity_obj);
        }
    }
}

static void timer_set_time_create(void)
{
    lv_obj_t * obj = lv_watch_obj_create(lv_watch_get_activity_obj(ACT_ID_TIMER_LIST));

    hal_rtc_t time;
    Hal_Mem_Set(&time, 0, sizeof(hal_rtc_t));
    timer_set_time_roller_create(obj, &time);
    timer_set_time_opt_create(obj);
}

static lv_obj_t * timer_set_time_roller_create(lv_obj_t * par, hal_rtc_t * date_time)
{
    lv_obj_t * content;
    lv_obj_t * roller1_cont;
    lv_obj_t * roller2_cont;
    lv_obj_t * roller3_cont;
    lv_obj_t * roller1;
    lv_obj_t * roller2;
    lv_obj_t * roller3;
    lv_obj_t * label1;
    lv_obj_t * label2;

    lv_timer_list_obj_ext_t * ext = timer_list_get_ext();

    lv_obj_t * label = lv_label_create(par, NULL);
    lv_label_set_text_id(label, WATCH_TEXT_ID_TIME_SETTING);
    lv_obj_add_style(label, LV_LABEL_PART_MAIN, &lv_watch_font20_gray);
    lv_label_set_long_mode(label, LV_LABEL_LONG_BREAK);
    lv_obj_set_height(label, LV_VER_RES/5);
    lv_obj_align(label, NULL, LV_ALIGN_IN_TOP_MID, 0, 10);

    content =  lv_cont_create(par, NULL);
    lv_obj_set_size(content, LV_HOR_RES, LV_VER_RES / 2);
    lv_obj_set_pos(content, 0, lv_obj_get_height(label));
    lv_cont_set_layout(content, LV_LAYOUT_ROW_MID);
    lv_obj_add_style(content, LV_CONT_PART_MAIN, &lv_style_transp);
    lv_obj_set_style_local_pad_top(content, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 2);
    lv_obj_set_style_local_pad_bottom(content, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 2);
    lv_obj_set_style_local_pad_left(content, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 15);
    lv_obj_set_style_local_pad_right(content, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 15);
    lv_obj_set_style_local_pad_inner(content, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5);
    lv_obj_align(content, label, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
    lv_obj_set_base_dir(content, LV_BIDI_DIR_LTR);

    /*roller1 hour*/
    roller1_cont = lv_cont_create(content, NULL);
    lv_obj_set_size(roller1_cont, LV_HOR_RES / 4, LV_VER_RES / 2);
    lv_cont_set_layout(roller1_cont, LV_LAYOUT_OFF);
    lv_obj_add_style(roller1_cont, LV_CONT_PART_MAIN, &lv_watch_style_transp);

    roller1 = lv_roller_create(roller1_cont, NULL);
    ext->roller[0] = roller1;
    lv_obj_add_style(roller1, LV_ROLLER_PART_BG, &lv_watch_font20_gray);
    lv_obj_set_style_local_text_line_space(roller1, LV_ROLLER_PART_BG, LV_STATE_DEFAULT, 20);
    lv_obj_set_style_local_bg_opa(roller1, LV_ROLLER_PART_BG, LV_STATE_DEFAULT, LV_OPA_TRANSP);
    lv_obj_add_style(roller1, LV_ROLLER_PART_SELECTED, &lv_watch_font20_gray);
    lv_obj_set_style_local_text_line_space(roller1, LV_ROLLER_PART_SELECTED, LV_STATE_DEFAULT, 20);
    lv_obj_set_style_local_bg_opa(roller1, LV_ROLLER_PART_SELECTED, LV_STATE_DEFAULT, LV_OPA_TRANSP);
    lv_roller_set_visible_row_count(roller1, 3);

    /*label1*/
    label1 = lv_label_create(content, NULL);
    lv_obj_set_style_local_text_font(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_THEME_WATCH_NIGHT_FONT_BIG);
    lv_obj_set_style_local_text_color(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE);
    lv_label_set_text(label1, ":");

    /*roller2 minute*/
    roller2_cont = lv_cont_create(content, roller1_cont);
    roller2 = lv_roller_create(roller2_cont, NULL);
    ext->roller[1] = roller2;
    lv_obj_add_style(roller2, LV_ROLLER_PART_BG, &lv_watch_font20_gray);
    lv_obj_set_style_local_text_line_space(roller2, LV_ROLLER_PART_BG, LV_STATE_DEFAULT, 20);
    lv_obj_set_style_local_bg_opa(roller2, LV_ROLLER_PART_BG, LV_STATE_DEFAULT, LV_OPA_TRANSP);
    lv_obj_add_style(roller2, LV_ROLLER_PART_SELECTED, &lv_watch_font20_gray);
    lv_obj_set_style_local_text_line_space(roller2, LV_ROLLER_PART_SELECTED, LV_STATE_DEFAULT, 20);
    lv_obj_set_style_local_bg_opa(roller2, LV_ROLLER_PART_SELECTED, LV_STATE_DEFAULT, LV_OPA_TRANSP);
    lv_roller_set_visible_row_count(roller2, 3);

    /*label2*/
    label2 = lv_label_create(content, label1);
    lv_label_set_text(label2, ":");

    /*roller3 second*/
    roller3_cont = lv_cont_create(content, roller1_cont);
    roller3 = lv_roller_create(roller3_cont, NULL);
    ext->roller[2] = roller3;
    lv_obj_add_style(roller3, LV_ROLLER_PART_BG, &lv_watch_font20_gray);
    lv_obj_set_style_local_text_line_space(roller3, LV_ROLLER_PART_BG, LV_STATE_DEFAULT, 20);
    lv_obj_set_style_local_bg_opa(roller3, LV_ROLLER_PART_BG, LV_STATE_DEFAULT, LV_OPA_TRANSP);
    lv_obj_add_style(roller3, LV_ROLLER_PART_SELECTED, &lv_watch_font20_gray);
    lv_obj_set_style_local_text_line_space(roller3, LV_ROLLER_PART_SELECTED, LV_STATE_DEFAULT, 20);
    lv_obj_set_style_local_bg_opa(roller3, LV_ROLLER_PART_SELECTED, LV_STATE_DEFAULT, LV_OPA_TRANSP);
    lv_roller_set_visible_row_count(roller3, 3);

    lv_watch_set_free_num(roller1, 1);
    lv_watch_set_free_num(roller2, 2);
    lv_watch_set_free_num(roller3, 3);

    /*set time*/
    char hour_opt[16];
    char min_opt[16];
    char sec_opt[16];
    memset(hour_opt, 0, 16);
    memset(min_opt, 0, 16);
    memset(sec_opt, 0, 16);

    timer_get_optstr_from_num(date_time->tm_hour, hour_opt, 2);
    timer_get_optstr_from_num(date_time->tm_min, min_opt, 3);
    timer_get_optstr_from_num(date_time->tm_sec, sec_opt, 3);

    lv_roller_set_options(roller1, hour_opt, LV_ROLLER_MODE_NORMAL);
    lv_obj_set_event_cb(roller1,  timer_set_time_roller_action);

    lv_roller_set_options(roller2, min_opt, LV_ROLLER_MODE_NORMAL);
    lv_obj_set_event_cb(roller2, timer_set_time_roller_action);

    lv_roller_set_options(roller3, sec_opt, LV_ROLLER_MODE_NORMAL);
    lv_obj_set_event_cb(roller3, timer_set_time_roller_action);

    lv_watch_set_free_num(content, 2);

    char str[3] = "00";
    memset(ext->sel_str[0],0,6);
    memset(ext->sel_str[1],0,6);
    memset(ext->sel_str[2],0,6);
    strncpy(ext->sel_str[0],str,2);
    strncpy(ext->sel_str[1],str,2);
    strncpy(ext->sel_str[2],str,2);
    lv_roller_set_auto_fit(roller1, true);
    lv_roller_set_selected(roller1, 2, LV_ANIM_OFF);
    lv_watch_roller_set_sel_style(roller1, ext->sel_str[0]);

    lv_roller_set_auto_fit(roller2, true);
    lv_roller_set_selected(roller2, 2, LV_ANIM_OFF);
    lv_watch_roller_set_sel_style(roller2, ext->sel_str[1]);

    lv_roller_set_auto_fit(roller3, true);
    lv_roller_set_selected(roller3, 2, LV_ANIM_OFF);
    lv_watch_roller_set_sel_style(roller3, ext->sel_str[2]);

    return content;
}

static void timer_set_time_roller_action(lv_obj_t * roller, lv_event_t e)
{
    if(LV_EVENT_VALUE_CHANGED != e) {
        return;
    }

    char sel_str[4];
    char hour_opt[16];
    char min_opt[16];
    char sec_opt[16];
    uint8_t hour;
    uint8_t min;
    uint8_t sec;

    memset(sel_str, 0, 4);
    memset(hour_opt, 0, 16);
    memset(min_opt, 0, 16);
    memset(sec_opt, 0, 16);

    lv_roller_get_selected_str(roller, sel_str, 4);

    if(lv_obj_get_user_data(roller).user_num == 1) {
        hour = atoi(sel_str);
        timer_get_optstr_from_num(hour, hour_opt, 2);
        lv_roller_set_options(roller, hour_opt, LV_ROLLER_MODE_NORMAL);
        lv_roller_set_selected(roller, 2, false);
    } else if(lv_obj_get_user_data(roller).user_num == 2) {
        min = atoi(sel_str);
        timer_get_optstr_from_num(min, min_opt, 3);
        lv_roller_set_options(roller, min_opt, LV_ROLLER_MODE_NORMAL);
        lv_roller_set_selected(roller, 2, false);
    } else if(lv_obj_get_user_data(roller).user_num == 3) {
        sec = atoi(sel_str);
        timer_get_optstr_from_num(sec, sec_opt, 3);
        lv_roller_set_options(roller, sec_opt, LV_ROLLER_MODE_NORMAL);
        lv_roller_set_selected(roller, 2, false);
    }
}

static lv_obj_t * timer_set_time_opt_create(lv_obj_t * par)
{
    lv_obj_t * btn;
    btn = lv_imgbtn_create(par, NULL);
    lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_TIMER_STOP);
    lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_TIMER_STOP);
    lv_obj_align(btn, NULL, LV_ALIGN_IN_BOTTOM_LEFT, LV_HOR_RES*90/360, -LV_VER_RES*40/360);
    lv_obj_set_event_cb(btn, timer_set_time_cancel_btn_action);

    btn = lv_imgbtn_create(par, NULL);
    lv_imgbtn_set_src(btn, LV_BTN_STATE_RELEASED, ICON_TIMER_START);
    lv_imgbtn_set_src(btn, LV_BTN_STATE_PRESSED, ICON_TIMER_START);
    lv_obj_align(btn, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, -LV_HOR_RES*90/360, -LV_VER_RES*40/360);
    lv_obj_set_event_cb(btn, timer_set_time_ok_btn_action);

    return par;
}

static void timer_set_time_cancel_btn_action(lv_obj_t * obj, lv_event_t e)
{
    if(LV_EVENT_CLICKED != e) {
        return;
    }

    //recreate timer list UI
    lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_TIMER_LIST);
    if(activity_obj) {
        timer_list_prepare_destory(activity_obj);
        lv_obj_del(activity_obj);
    }

    timer_list_create(NULL);
}

static void timer_set_time_ok_btn_action(lv_obj_t * obj, lv_event_t e)
{
    if(LV_EVENT_CLICKED != e) {
        return;
    }

    //get time
    char sel_str1[4];
    char sel_str2[4];
    char sel_str3[4];
    memset(sel_str1, 0, 4);
    memset(sel_str2, 0, 4);
    memset(sel_str3, 0, 4);

    lv_timer_list_obj_ext_t * ext = timer_list_get_ext();
    lv_roller_get_selected_str(ext->roller[0], sel_str1, 4);
    lv_roller_get_selected_str(ext->roller[1], sel_str2, 4);
    lv_roller_get_selected_str(ext->roller[2], sel_str3, 4);
    uint8_t hour = atoi(sel_str1);
    uint8_t min = atoi(sel_str2);
    uint8_t sec = atoi(sel_str3);

    if((0 == hour) && (0 == min) && (0 == sec)) {
        printf("%s: timer 00:00:00 is illegal. \n", __FUNCTION__);
        return;
    }

    //del timer list UI
    lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_TIMER_LIST);
    if(activity_obj) {
        timer_list_prepare_destory(activity_obj);
        lv_obj_del(activity_obj);
    }

    //create timer UI
    timer_total = (hour * 3600 + min * 60 + sec) * 1000; //ms
    timer_remaining = timer_total;
    timer_create(NULL);
    timer_start_action(NULL, LV_EVENT_CLICKED);
}

static void timer_get_optstr_from_num(uint8_t num, char * stropt, uint8_t type)
{
    uint8_t opt_display[5];
    uint8_t i, pos = 0;
    uint8_t min = 1;
    uint8_t count = 0;
    if(type == 1) { //month
        count = 12;
    } else if(type == 2) { //hour
        min = 0;
        count = 24;
    } else if(type == 3) { //min&sec
        min = 0;
        count = 60;
    }

    opt_display[0] = ((num - min + count) - 2) % count + min;
    opt_display[1] = ((num - min + count) - 1) % count + min;
    opt_display[2] = num;
    opt_display[3] = (num + 1 - min) % count + min;
    opt_display[4] = (num + 2 - min) % count + min;


    for(i = 0; i < 5; i++) {
        stropt[pos++] = (opt_display[i] % 100) / 10 + 0x30;
        stropt[pos++] = opt_display[i] % 10 + 0x30;
        stropt[pos++] = 0x0A;
    }
    stropt [pos - 1] = 0;
}

#endif /*USE_LV_WATCH_TIMER*/
